%include "pm.inc";常量，宏 以及一些说明
;org 07c00h
org 0100h
    jmp LABEL_BEGIN

[SECTION .gdt]
;GDT                 段基址  段界限   属性

LABEL_GDT:  Descriptor  0,   0,         0 ;空描述符
LABEL_DESC_NORMAL: Descriptor   0,   0ffffh,    DA_DRW ;  NORMAL描述符
LABEL_DESC_CODE32: Descriptor   0,  SegCode32Len - 1, DA_C + DA_32;  
LABEL_DESC_CODE16: Descriptor   0,  0ffffh,     DA_C;  
LABEL_DESC_DATA: Descriptor     0,  DataLen-1,  DA_DRW; Data
LABEL_DESC_STACK: Descriptor    0,  TopOfStack,     DA_DRWA+DA_32;   
LABEL_DESC_VIDEO : Descriptor   0B8000h, 0ffffh,    DA_DRW+DA_DPL3;  显存首地址
LABEL_DESC_LDT  : Descriptor    0,  LDTLen - 1, DA_LDT;  LDT
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1,   DA_C + DA_32
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3,       DA_DRWA+DA_32+DA_DPL3
LABEL_DESC_TSS: Descriptor 0, TSSLen-1, DA_386TSS

;门 入口 门描述符只描述一个入口 代码的段还是由GDT定义

LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL3

GdtLen  equ  $-LABEL_GDT  ;GDT长度
GdtPtr  dw   GdtLen - 1  ;GDT 界限
dd 0  ; GDT基地址
;GdtPtr也是一个数据结构  前2字节是GDT界限  后4字节是GDT基地址

;GDT 选择子
SelectorNormal  equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32  equ     LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16  equ     LABEL_DESC_CODE16 - LABEL_GDT
SelectorData    equ     LABEL_DESC_DATA - LABEL_GDT
SelectorStack   equ     LABEL_DESC_STACK - LABEL_GDT
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT
SelectorVideo   equ LABEL_DESC_VIDEO - LABEL_GDT

SelectorCodeDest equ        LABEL_DESC_CODE_DEST - LABEL_GDT
SelectorCallGateTest equ    LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3
SelectorCodeRing3 equ       LABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3
SelectorStack3    equ       LABEL_DESC_STACK3- LABEL_GDT + SA_RPL3
SelectorTSS equ         LABEL_DESC_TSS - LABEL_GDT



; END of [SECTION .gdt]



[SECTION .data1]  ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode   dw  0;  字符串
PMMessage:  db "In Protect Mode now.^_^", 0;  在保护模式中显示
OffsetPMMessage equ PMMessage - $$
StrTest:	db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0;
OffsetStrTest	equ StrTest - $$
DataLen     equ $-LABEL_DATA
;end of[section.data1]


;全局堆栈段
[SECTION .gs]
ALIGN   32
[BITS 32]
LABEL_STACK:
    times 512 db 0
TopOfStack equ  $-LABEL_STACK-1

;end of [section.gs]


; 堆栈段ring3
[SECTION .s3]
ALIGN   32
[BITS 32]
LABEL_STACK3:
    times 512 db 0
TopOfStack3     equ $ - LABEL_STACK3 - 1
; END of [SECTION .s3]


;TSS
[SECTION .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
    DD  0       ;BACK
    DD  TopOfStack  ;0级堆栈
    DD  SelectorStack   ;
    DD  0       ;1级堆栈
    DD  0       ;
    DD  0       ;2级堆栈
    DD  0       ;
    DD  0       ;CR3
    DD  0       ;EIP
    DD  0       ;EFLAGS
    DD  0       ;EAX
    DD  0       ;ECX
    DD  0       ;EDX
    DD  0       ;EBX
    DD  0       ;ESP
    DD  0       ;EBP
    DD  0       ;ESI
    DD  0       ;EDI
    DD  0       ;ES
    DD  0       ;CS
    DD  0       ;SS     
    DD  0       ;DS
    DD  0       ;FS
    DD  0       ;GS
    DD  0       ;LDT
    DW  0       ;调试陷阱标志
    DW  $-LABEL_TSS + 2  ;IO位图基址
    DB  0ffh        ;IO位图结束标示
TSSLen  equ $-LABEL_TSS




;这是一个16位代码段 这个程序修改了gdt中的一些值 然后执行跳转到第三个section
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0100h

    mov [LABEL_GO_BACK_TO_REAL+3], ax
    mov     [SPValueInRealMode],sp

    ;初始化16位的代码段描述符
    xor eax, eax
    mov     ax, cs
    shl eax, 4
    add eax, LABEL_SEG_CODE16
    mov word [LABEL_DESC_CODE16 +2], ax
    shr eax, 16
    mov     byte [LABEL_DESC_CODE16 +4], al
    mov byte [LABEL_DESC_CODE16 +7], ah

    ;初始化32位代码段描述符
    xor eax, eax
    mov ax, cs
    shl eax, 4
    add eax, LABEL_SEG_CODE32
    mov word [LABEL_DESC_CODE32 + 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_CODE32 + 4], al
    mov byte [LABEL_DESC_CODE32 + 7], ah

    ; 初始化数据段描述符  
        xor eax, eax  
        mov ax, ds  
        shl eax, 4  
        add eax, LABEL_DATA  
        mov word [LABEL_DESC_DATA + 2], ax  
        shr eax, 16  
    mov byte [LABEL_DESC_DATA + 4], al  
    mov byte [LABEL_DESC_DATA + 7], ah  

        ; 初始化堆栈段描述符  
        xor eax, eax  
        mov ax, ds  
        shl eax, 4  
        add eax, LABEL_STACK  
        mov word [LABEL_DESC_STACK + 2], ax  
        shr eax, 16  
        mov byte [LABEL_DESC_STACK + 4], al  
        mov byte [LABEL_DESC_STACK + 7], ah  

    ;初始化LDT在GDT中的描述符
    xor eax, eax  
        mov ax, ds  
        shl eax, 4  
        add eax, LABEL_LDT  
        mov word [LABEL_DESC_LDT + 2], ax  
        shr eax, 16  
        mov byte [LABEL_DESC_LDT + 4], al  
        mov byte [LABEL_DESC_LDT + 7], ah  

    ; 初始化 LDT 中的描述符  
        xor eax, eax  
        mov ax, ds  
        shl eax, 4  
        add eax, LABEL_CODE_A  
        mov word [LABEL_LDT_DESC_CODEA + 2], ax  
        shr eax, 16  
        mov byte [LABEL_LDT_DESC_CODEA + 4], al  
        mov byte [LABEL_LDT_DESC_CODEA + 7], ah

    ;初始化 测试调用门的代码段描述符
        xor eax, eax  
        mov ax, cs  
        shl eax, 4  
        add eax, LABEL_SEG_CODE_DEST  
        mov word [LABEL_DESC_CODE_DEST + 2], ax  
        shr eax, 16  
        mov byte [LABEL_DESC_CODE_DEST + 4], al  
        mov byte [LABEL_DESC_CODE_DEST + 7], ah

    ; 初始化堆栈段描述符(Ring3)
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_STACK3
    mov word [LABEL_DESC_STACK3 + 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_STACK3 + 4], al
    mov byte [LABEL_DESC_STACK3 + 7], ah


    ; 初始化Ring3描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_CODE_RING3
    mov word [LABEL_DESC_CODE_RING3 + 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_CODE_RING3 + 4], al
    mov byte [LABEL_DESC_CODE_RING3 + 7], ah


    ; 初始化TSS描述符  
        xor eax, eax  
        mov ax, ds  
        shl eax, 4  
        add eax, LABEL_TSS  
        mov word [LABEL_DESC_TSS + 2], ax  
        shr eax, 16  
    mov byte [LABEL_DESC_TSS + 4], al  
    mov byte [LABEL_DESC_TSS + 7], ah  

    ;为加载GDTR作准备
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_GDT; eax <- gdt基地址
    mov     dword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址

    ;加载GDTR
    lgdt    [GdtPtr]

    ;关中断
    cli

    ;打开地址线A20
    in  al, 92h
    or  al, 00000010b
    out 92h, al

    ;准备切换到保护模式
    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    ;真正进入保护模式
    jmp dword   SelectorCode32:0 ;  执行这句会把SelectorCode32装入CS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:   ;从保护模式跳回到实模式就到了这里
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax

    mov     sp, [SPValueInRealMode]   ;指针调到了堆栈中  返回到实模式

    in  al, 92h
    and al, 11111101b;  关闭A20地址线
    out 92h, al

    sti;  开中断

    mov ax, 4c00h;
    int 21h;    回到dos
;end of section .s16




[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:
    mov ax, SelectorData
    mov ds, ax;         数据段选择子

    mov ax, SelectorVideo;  视频段选择子
    mov gs, ax

    mov ax, SelectorStack
    mov ss, ax;         堆栈段选择子

    mov esp, TopOfStack

    ;下面显示一个字符串
    mov ah, 0Ch;        0000黑底 1100红字
    xor esi, esi
    xor edi, edi
    mov esi, OffsetPMMessage;   源数据偏移
    mov edi, (80*10 + 10) * 2;  目的数据偏移  屏幕第10行第0列
    cld             ;告诉字符串向前移动

.1:
    lodsb       ;将字符串中的si指针所指向的一个字节装入al中
    test    al, al  ;判断al是否为空  为空跳转到2
    jz  .2
    mov [gs:edi], ax ;不为空显示当前字符
    add edi, 2    ;edi加二  
    jmp .1

.2: ;当第一条字符串显示完毕
    call    DispReturn      ;回车 换行

    mov ax, SelectorTSS
    ltr ax

    push    SelectorStack3
    push    TopOfStack3
    push    SelectorCodeRing3
    push    0
    retf

;因为跳转到特权级三  相当于任务已经切换 所以下面四行的代码执行不到了
;调用门  需要放到ring3  的代码底部   当打印出3  之后  开始调用门
;因为此时CPL是3  为了能访问到调用门  需要将调用门的 DPL  RPL也设置为3
    call    SelectorCallGateTest:0  ;测试调用门   

    ; Load ldt
    mov ax, SelectorLDT
    lldt    ax
    jmp SelectorLDTCodeA:0   ; 跳入局部任务

;-------------------------------------------------------------------------
DispReturn:
    push    eax
    push    ebx
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh

    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop ebx 
    pop eax

    ret
;disreturn 结束-------------------------------------------

SegCode32Len    equ $-LABEL_SEG_CODE32
;end of section .s32


[SECTION .sdest]; 调用门目标段
[BITS 32]
LABEL_SEG_CODE_DEST:
    ;jmp    $
    mov ax, SelectorVideo
    mov gs, ax  
    mov edi, (80*11+0)*2
    mov ah, 0Ch
    mov al, 'G'
    mov [gs:edi], ax

    ;load LDT  new  add
    mov ax, SelectorLDT
    lldt    ax
    jmp SelectorLDTCodeA:0

    retf

SegCodeDestLen equ  $-LABEL_SEG_CODE_DEST

;16位的代码段 由32位代码段跳入  跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:

    ;跳回实模式
    mov ax, SelectorNormal
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov eax, cr0
    and al, 11111110b
    mov cr0, eax

LABEL_GO_BACK_TO_REAL:
    jmp 0:LABEL_REAL_ENTRY;    段地址会在程序开始处被设置为正确的值

Code16Len equ $ - LABEL_SEG_CODE16

;end of section .s16code

;LDT
[SECTION .ldt]
ALIGN   32
LABEL_LDT:
;               段基址 段界限     属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1,   DA_C + DA_32

LDTLen  equ $ - LABEL_LDT

;ldt 选择子
SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
; end of section .ldt

;codeA  (ldt  32位代码段)
[SECTION .la]
ALIGN   32
[BITS   32]
LABEL_CODE_A:
    mov ax, SelectorVideo
    mov gs, ax

    mov edi, (80 * 12 + 0) * 2 ;  
    mov ah, 0Ch
    mov al, 'Z'
    mov [gs:edi], ax

    jmp SelectorCode16:0
CodeALen    equ $ - LABEL_CODE_A
;end of section .la


; CodeRing3
[SECTION .ring3]
ALIGN   32
[BITS 32]
LABEL_CODE_RING3:
    mov ax, SelectorVideo
    mov gs, ax

    mov edi, (80 * 14 + 0) * 2
    mov ah, 0Ch
    mov al, '3'
    mov [gs:edi], ax

    call    SelectorCallGateTest:0       ;从3特权级进入门   门中是ldt  Z

    jmp $
SegCodeRing3Len equ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]
;------------------------------------------
;nasm 03_privilege_switch.asm -o 03_privilege_switch.com
;bochs 启动freedos,格式化B盘
;sudo mount -o loop fblank.img /mnt/floppy
;sudo cp 03_privilege_switch.com /mnt/floppy
;sudo umount /mnt/floppy
;利用nasm编译该代码文件生成bin文件
;在生成的映像文件1fe和1ff改为0x55aa
;然后再将该bin文件拷贝到img的映像上面，最后在bochs上运行
;------------------------------------------